use proc_macro::TokenStream;
use quote::quote;
use syn::{
    Data, DeriveInput, Fields, Ident, LitBool, LitInt, LitStr, Result, Token,
    parse::{Parse, ParseStream},
    parse_macro_input,
};

/// 宏参数解析结构
///
/// 用于解析地理信息处理宏的参数，支持以下配置项：
/// - `transform`: 是否进行坐标系转换
/// - `srid`: 目标坐标系统标识符 (SRID)
/// - `db_field`: 数据库中几何字段的名称
/// - `field`: 生成字段的名称
struct MacroArgs {
    transform: Option<bool>,
    srid: Option<i32>,
    db_field: Option<String>,
    field: Option<Ident>,
}

impl Parse for MacroArgs {
    fn parse(input: ParseStream) -> Result<Self> {
        let mut transform = None;
        let mut srid = None;
        let mut db_field = None;
        let mut field = None;

        while !input.is_empty() {
            let lookahead = input.lookahead1();
            if lookahead.peek(syn::Ident) {
                let ident: Ident = input.parse()?;
                match ident.to_string().as_str() {
                    "transform" => {
                        input.parse::<Token![=]>()?;
                        let value = input.parse::<LitBool>()?.value();
                        transform = Some(value);
                    }
                    "srid" => {
                        input.parse::<Token![=]>()?;
                        let value = input.parse::<LitInt>()?.base10_parse::<i32>()?;
                        srid = Some(value);
                    }
                    "db_field" => {
                        input.parse::<Token![=]>()?;
                        let value = input.parse::<LitStr>()?.value();
                        db_field = Some(value);
                    }
                    "field" => {
                        input.parse::<Token![=]>()?;
                        field = Some(input.parse::<Ident>()?);
                    }
                    _ => return Err(syn::Error::new(ident.span(), "未知参数")),
                }
            } else {
                return Err(lookahead.error());
            }

            if input.peek(Token![,]) {
                input.parse::<Token![,]>()?;
            }
        }

        Ok(MacroArgs {
            transform,
            srid,
            db_field,
            field,
        })
    }
}

/// 为结构体添加几何中心点字段的宏
///
/// 此宏会自动为结构体添加一个包含几何对象中心点的字段，使用 PostGIS 的 `ST_Centroid` 函数计算。
///
/// ## 参数
///
/// - `transform`: 是否进行坐标系转换（默认: `true`）
/// - `srid`: 目标坐标系 SRID（默认: `4326` - WGS84）
/// - `db_field`: 数据库中几何字段名称（默认: `"geom"`）
/// - `field`: 生成的中心点字段名称（默认: `centroid`）
///
/// ## 示例
///
/// ```rust
/// use my_macros::with_centroid;
///
/// // 使用默认参数
/// #[with_centroid]
/// #[derive(Debug)]
/// struct Building {
///     id: i32,
///     name: String,
/// }
/// // 生成的结构体将包含 centroid: String 字段
///
/// // 自定义参数
/// #[with_centroid(transform = false, field = center, db_field = "geometry")]
/// struct Location {
///     id: i32,
///     address: String,
/// }
/// // 生成的结构体将包含 center: String 字段
/// ```
///
/// ## 生成的 SQL
///
/// 当 `transform = true` 时：
/// ```sql
/// ST_AsText(ST_Transform(ST_Centroid(geom), 4326))
/// ```
///
/// 当 `transform = false` 时：
/// ```sql
/// ST_AsText(ST_Centroid(geom))
/// ```
#[proc_macro_attribute]
pub fn with_centroid(args: TokenStream, input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let vis = &input.vis;
    let attrs = &input.attrs;

    let args = if args.is_empty() {
        MacroArgs {
            transform: None,
            srid: None,
            db_field: None,
            field: None,
        }
    } else {
        parse_macro_input!(args as MacroArgs)
    };

    let transform = args.transform.unwrap_or(true);
    let srid = args.srid.unwrap_or(4326);
    let db_field = args.db_field.unwrap_or_else(|| "geom".to_string());
    let field = args
        .field
        .unwrap_or_else(|| Ident::new("centroid", name.span()));

    let center_expr = if transform {
        format!("Expr::cust(\"ST_AsText(ST_Transform(ST_Centroid({db_field}), {srid}))\")")
    } else {
        format!("Expr::cust(\"ST_AsText(ST_Centroid({db_field}))\")")
    };

    if let Data::Struct(data_struct) = &input.data {
        if let Fields::Named(fields) = &data_struct.fields {
            let existing_fields = &fields.named;

            let expanded = quote! {
                #(#attrs)*
                #vis struct #name {
                    #existing_fields
                    #[sea_orm(from_expr = #center_expr)]
                    pub #field: Option<String>,
                }
            };

            return TokenStream::from(expanded);
        }
    }

    TokenStream::from(quote! { #input })
}

/// 为结构体添加完整几何图形字段的宏
///
/// 此宏会自动为结构体添加一个包含完整几何对象的字段，以 WKT (Well-Known Text) 格式返回。
///
/// ## 参数
///
/// - `transform`: 是否进行坐标系转换（默认: `true`）
/// - `srid`: 目标坐标系 SRID（默认: `4326` - WGS84）
/// - `db_field`: 数据库中几何字段名称（默认: `"geom"`）
/// - `field`: 生成的几何字段名称（默认: `geometry`）
///
/// ## 示例
///
/// ```rust
/// use my_macros::with_geometry;
///
/// // 使用默认参数
/// #[with_geometry]
/// #[derive(Debug)]
/// struct Polygon {
///     id: i32,
///     name: String,
/// }
/// // 生成的结构体将包含 geometry: String 字段
///
/// // 自定义参数
/// #[with_geometry(srid = 3857, field = shape, db_field = "polygon_geom")]
/// struct District {
///     id: i32,
///     district_name: String,
/// }
/// // 生成的结构体将包含 shape: String 字段
/// ```
///
/// ## 生成的 SQL
///
/// 当 `transform = true` 时：
/// ```sql
/// ST_AsText(ST_Transform(geom, 4326))
/// ```
///
/// 当 `transform = false` 时：
/// ```sql
/// ST_AsText(geom)
/// ```
#[proc_macro_attribute]
pub fn with_geometry(args: TokenStream, input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let vis = &input.vis;
    let attrs = &input.attrs;

    let args = if args.is_empty() {
        MacroArgs {
            transform: None,
            srid: None,
            db_field: None,
            field: None,
        }
    } else {
        parse_macro_input!(args as MacroArgs)
    };

    let transform = args.transform.unwrap_or(true);
    let srid = args.srid.unwrap_or(4326);
    let db_field = args.db_field.unwrap_or_else(|| "geom".to_string());
    let field = args
        .field
        .unwrap_or_else(|| Ident::new("geometry", name.span()));

    let geometry_expr = if transform {
        format!("Expr::cust(\"ST_AsText(ST_Transform({db_field}, {srid}))\")")
    } else {
        format!("Expr::cust(\"ST_AsText({db_field})\")")
    };

    if let Data::Struct(data_struct) = &input.data {
        if let Fields::Named(fields) = &data_struct.fields {
            let existing_fields = &fields.named;

            let expanded = quote! {
                #(#attrs)*
                #vis struct #name {
                    #existing_fields
                    #[sea_orm(from_expr = #geometry_expr)]
                    pub #field: Option<String>,
                }
            };

            return TokenStream::from(expanded);
        }
    }

    TokenStream::from(quote! { #input })
}

#[proc_macro_attribute]
pub fn with_geo(args: TokenStream, input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let vis = &input.vis;
    let attrs = &input.attrs;

    let args = if args.is_empty() {
        MacroArgs {
            transform: None,
            srid: None,
            db_field: None,
            field: None,
        }
    } else {
        parse_macro_input!(args as MacroArgs)
    };

    let transform = args.transform.unwrap_or(true);
    let srid = args.srid.unwrap_or(4326);
    let db_field = args.db_field.unwrap_or_else(|| "geom".to_string());

    let centroid_field = Ident::new("centroid", name.span());
    let geometry_field = Ident::new("geometry", name.span());

    let center_expr = if transform {
        format!("Expr::cust(\"ST_AsText(ST_Transform(ST_Centroid({db_field}), {srid}))\")")
    } else {
        format!("Expr::cust(\"ST_AsText(ST_Centroid({db_field}))\")")
    };

    let geometry_expr = if transform {
        format!("Expr::cust(\"ST_AsText(ST_Transform({db_field}, {srid}))\")")
    } else {
        format!("Expr::cust(\"ST_AsText({db_field})\")")
    };

    if let Data::Struct(data_struct) = &input.data {
        if let Fields::Named(fields) = &data_struct.fields {
            let existing_fields = &fields.named;

            let expanded = quote! {
                #(#attrs)*
                #vis struct #name {
                    #existing_fields
                    #[sea_orm(from_expr = #center_expr)]
                    pub #centroid_field: Option<String>,
                    #[sea_orm(from_expr = #geometry_expr)]
                    pub #geometry_field: Option<String>,
                }
            };

            return TokenStream::from(expanded);
        }
    }

    TokenStream::from(quote! { #input })
}
